#include "Factory.h"
#include "Multigrid.h"
#include <string>
#include <iostream>
#include <memory>
#include <tuple>

using namespace std;

typedef SingletonHolder<Factory<AbstractMultigrid, tuple<string, string, string, string> > > MultigridFactory;
typedef SingletonHolder<Factory<TriElement, string> > ElementFactory;
typedef SingletonHolder<Factory<AbstractPoisson, string> > PoissonFactory;

namespace
{
	template
	<
		typename Element = P1Element, 
		template <typename, template <typename> class, template <typename> class> class Cycle = VCycle,
		template <typename> class Restriction = FullWeighting,
		template <typename> class Interpolation = Linear
	>
	shared_ptr<AbstractMultigrid> CreateMultigrid()
	{
		return shared_ptr<Multigrid<Element, Cycle, Restriction, Interpolation> >(new Multigrid<Element, Cycle, Restriction, Interpolation>);
	}

	shared_ptr<TriElement> CreateP1Element()
	{
		return shared_ptr<P1Element>(new P1Element);
	}
	
	shared_ptr<TriElement> CreateP2Element()
	{
		return shared_ptr<P2Element>(new P2Element);
	}

	template <typename Element = P1Element>
	shared_ptr<AbstractPoisson> CreatePoisson()
	{
		return shared_ptr<Poisson<Element> >(new Poisson<Element>);
	}
}

void Register()
{
	const bool register1 = MultigridFactory::Instance().Register(std::make_tuple("P1Element", "VCycle", "FullWeighting", "Linear"), CreateMultigrid<P1Element, VCycle, FullWeighting, Linear>);
	const bool register2 = MultigridFactory::Instance().Register(std::make_tuple("P1Element", "VCycle", "FullWeighting", "Quadratic"), CreateMultigrid<P1Element, VCycle, FullWeighting, Quadratic>);
	const bool register3 = MultigridFactory::Instance().Register(std::make_tuple("P1Element" , "VCycle", "Injection", "Linear"), CreateMultigrid<P1Element, VCycle, Injection, Linear>);
	const bool register4 = MultigridFactory::Instance().Register(std::make_tuple("P1Element", "VCycle", "Injection", "Quadratic"), CreateMultigrid<P1Element, VCycle, Injection, Quadratic>);
	const bool register5 = MultigridFactory::Instance().Register(std::make_tuple("P1Element", "FullMultigridVCycle", "FullWeighting", "Linear"), CreateMultigrid<P1Element, FullMultigridVCycle, FullWeighting, Linear>);
	const bool register6 = MultigridFactory::Instance().Register(std::make_tuple("P1Element", "FullMultigridVCycle", "FullWeighting", "Quadratic"), CreateMultigrid<P1Element, FullMultigridVCycle, FullWeighting, Quadratic>);
	const bool register7 = MultigridFactory::Instance().Register(std::make_tuple("P1Element", "FullMultigridVCycle", "Injection", "Linear"), CreateMultigrid<P1Element, FullMultigridVCycle, Injection, Linear>);
	const bool register8 = MultigridFactory::Instance().Register(std::make_tuple("P1Element", "FullMultigridVCycle", "Injection", "Quadratic"), CreateMultigrid<P1Element, FullMultigridVCycle, Injection, Quadratic>);

	const bool register9 = MultigridFactory::Instance().Register(std::make_tuple("P2Element", "VCycle", "FullWeighting", "Linear"), CreateMultigrid<P2Element, VCycle, FullWeighting, Linear>);
	const bool register10 = MultigridFactory::Instance().Register(std::make_tuple("P2Element", "VCycle", "FullWeighting", "Quadratic"), CreateMultigrid<P2Element, VCycle, FullWeighting, Quadratic>);
	const bool register11 = MultigridFactory::Instance().Register(std::make_tuple("P2Element", "VCycle", "Injection", "Linear"), CreateMultigrid<P2Element, VCycle, Injection, Linear>);
	const bool register12 = MultigridFactory::Instance().Register(std::make_tuple("P2Element", "VCycle", "Injection", "Quadratic"), CreateMultigrid<P2Element, VCycle, Injection, Quadratic>);
	const bool register13 = MultigridFactory::Instance().Register(std::make_tuple("P2Element", "FullMultigridVCycle", "FullWeighting", "Linear"), CreateMultigrid<P2Element, FullMultigridVCycle, FullWeighting, Linear>);
	const bool register14 = MultigridFactory::Instance().Register(std::make_tuple("P2Element", "FullMultigridVCycle", "FullWeighting", "Quadratic"), CreateMultigrid<P2Element, FullMultigridVCycle, FullWeighting, Quadratic>);
	const bool register15 = MultigridFactory::Instance().Register(std::make_tuple("P2Element", "FullMultigridVCycle", "Injection", "Linear"), CreateMultigrid<P2Element, FullMultigridVCycle, Injection, Linear>);
	const bool register16 = MultigridFactory::Instance().Register(std::make_tuple("P2Element", "FullMultigridVCycle", "Injection", "Quadratic"), CreateMultigrid<P2Element, FullMultigridVCycle, Injection, Quadratic>);

	const bool register17 = ElementFactory::Instance().Register("P1Element", CreateP1Element);
	const bool register18 = ElementFactory::Instance().Register("P2Element", CreateP2Element);

	const bool register19 = PoissonFactory::Instance().Register("P1Element", CreatePoisson<P1Element>);
	const bool register20 = PoissonFactory::Instance().Register("P2Element", CreatePoisson<P2Element>);
}

void UnRegister()
{

}

